Hoisting
Hoisting 이란?
자바스크립트 엔진은 코드를 실행할 때 C언어와 마찬가지로 컴파일을 하는 과정을 거친다. 이 과정을 거치면서, 호이스팅 현상이 발생하게 된다. 컴파일 하는 과정은 여러가지 동작이 있지만 그 중에서도 눈여겨 봐야 할 것은 environmentRecord 이다.
자바 스크립트 엔진은 코드가 실행 되기 전에 environmentRecord에 현재 컨텍스트와 관련된 코드의 식별자 정보들이 저장한다. 컨텍스트 내부 전체를 처음부터 끝까지 쭉 훑어나가며 순서대로 수집한다.
자바스크립트 엔진은 이 수집한 정보들을 해당 컨텍스트 즉, 해당 스코프의 최상단에 끌어 올려놓는다. 이 동작을 호이스팅 이라 한다.
예를 보자면 원본 코드는 이렇다.
function a(x) {
// 수집대상 1(매개변수)
console.log(x); // 1
var x; // 수집대상 2(변수 선언)
console.log(x); // undefined
var x = 2; // 수집대상 3(변수 선언)
console.log(x); // 2
}
a(1);
자바스크립트는 a 함수 호출을 실행 하기 전 코드를 위에서 부터 쭉 훑어 나갈 것이다. 이때 environmentRecord에 식별자 정보가 담길 것이다. 호이스팅이 발생 하면서, 결국 이 코드와 똑같이 될 것이다.
function a() {
var x; // 수집대상 1의 변수 선언 부분
var x; // 수집대상 2의 변수 선언 부분
var x; // 수집대상 3의 변수 선언 부분
x = 1; //수집대상 1의 할당 부분
console.log(x); // 1
console.log(x); // 1
x = 2; // 수집대상 3의 할당 부분
console.log(x); // 2
}
a();
environmentRecord는 실행될 컨텍스트의 대상 코드 내에 어떤 식별자들이 있는지에만 관심이 있으므로, 선언문만 위로 쭉 올려준다.(할당문은 남겨둔다.)
이때, var와 let, const 식별자에 따른 호이스팅 동작이 다르게 되는데 한번 알아보자.
var, let, const 호이스팅 동작
- var
console.log(a); // undefined
var a = "호이스팅";
먼저, var의 호이스팅 동작 이다. 자바스크립트 엔진은 컨텍스트를 실행하기 전, 컨텍스트를 위에서 쭉 훑으면서 변수 선언문을 올릴 것이다. 그렇게 된다면
var a;
console.log(a); // undefiend
a = "호이스팅";
이렇게 될 것이다. a를 출력했을 때, 선언문만 올려지기 때문에 undefined가 출력이 될 것이다.
- let, const
그렇다면 이번엔 let, const도 똑같이 동작을 할지 알아보자.
console.log(b);
let b = 5;
console.log(b);
const b = 5;
우리는 당연히 호이스팅이 일어나기 때문에, 선언문만 올려져서 var와 마찬가지로 undefined가 출력 되지 않을까 라는 생각을 할 것이다. 하지만 실제로는 Reference Error가 발생한다. 왜 그럴까?
이를 이해하기 위해서는 변수가 어떻게 선언이 되고 할당이 되는지 과정을 알아봐야 한다. 먼저 변수는 프로그램 내에서 크게 3가지 단계를 거치게 된다.
- 선언 : 엔진이 코드를 훑으면서 식별자들에 대한 정보를 수집한다.
- 초기화 : 식별자에 메모리를 할당하고, undefined 상태를 부여한다.
- 할당 : 변수 안에 직접 값을 넘겨준다.
변수 및 함수는 선언과 초기화를 해주어야지만 값의 참조 및 할당이 가능하다. 그렇다면 var, const, let은 각각 호이스팅이 될 때 선언문이 끌어올린다고 했는데, 초기화가 되는 것일까?
var는 호이스팅이 발생 하면서 선언과 초기화가 거의 동시에 이루어진다. 그래서 호이스팅이 되었을 때, 값을 참조 및 할당이 가능함으로 위와 같은 결과가 나타나는 것이다.
하지만 const, let은 호이스팅이 발생 하면서 선언만 이루어지고 실행 시점에서 실질적인 선언부를 만날 때까지 초기화는 이루어지지 않는다. 실질적인 선언문을 만날 때까지 간극 만큼 해당 변수에 대한 메모리가 존재하지 않기 때문에 선언부 상단에서 참조, 할당이 불가능 하다. 그래서 Reference Error가 발생하는 것이다.
이때, 간극을 일시적 사각지대 TDZ(Temporal Dead Zone)이라 부르는데 이어서 알아보자.
TDZ(Temporal Dead Zone)
let, const는 호이스팅이 되면서 선언문이 끌어올려질 것이다. 하지만 초기화는 되어있지 않아 선언문 상단에서 참조, 할당이 불가능하다.
console.log(a);
let a;
이 코드를 실행을 하면 엔진이 코드를 읽으면서 EnvironmentRecord에 식별자 정보가 담기고, 선언문은 해당 스코프 위로 끌어 올려진다.
let a;
console.log(a);
그렇다면 선언문을 끌어 올려져 이 코드와 같은 것일까? 실제로는 맞지만, 기존에 있었던 선언문의 위치는 console 출력 코드 보다 아래줄에 있어 그 부분에 도달해야지만 참조 및 할당이 가능하기 때문에 실질적으론 처음의 코드와 같다.
댓글남기기